package com.egg.common.mybatis.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.egg.common.context.util.LocalThreadContextUtils;
import com.egg.common.core.constant.StringPool;
import com.egg.common.core.enums.ResponseCode;
import com.egg.common.core.exception.CustomException;
import com.egg.common.core.model.entity.BaseEntity;
import com.egg.common.mybatis.service.IBaseService;
import com.egg.common.util.IdGenUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 封装增删改查, 主要维护 6个标准字段
 * 内部有做简单的空判断
 * get 语意代表一定获取, 查询不到时, 则抛出异常
 * find 语意代表查询, 查询不到时,不抛出异常
 * 兼容jpa实体入参语法
 *
 * @author east
 */
@Slf4j
@Mapper
@NoArgsConstructor
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements IBaseService<T> {


    @Override
    public LambdaQueryWrapper<T> buildLambdaQw() {
        LambdaQueryWrapper<T> qw = Wrappers.lambdaQuery(entityClass);
        return qw;
    }

    @Override
    public LambdaQueryWrapper<T> buildLambdaSortQw() {
        LambdaQueryWrapper<T> qw = Wrappers.lambdaQuery(entityClass);
        qw.orderByDesc(T::getCreateDate);
        return qw;
    }

    @Override
    public String getTableName(Class<?> clazz) {
        if (clazz.isAnnotationPresent(TableName.class)) {
            return clazz.getAnnotation(TableName.class).value();
        } else {
            return StringUtils.camelToUnderline(clazz.getSimpleName());
        }
    }

    @Override
    public StringBuilder buildExistsSqlStr(
            Class<? extends BaseEntity> outTableClass
            , String outField
            , Class<? extends BaseEntity> inTableClass
            , String inField
    ) {
        return buildExistsSqlStr(getTableName(outTableClass), outField, getTableName(inTableClass), inField);
    }

    @Override
    public StringBuilder buildExistsSqlStr(
            String outTableName
            , String outField
            , String inTableName
            , String inField
    ) {
        StringBuilder exists = new StringBuilder();
        exists.append(" SELECT 1 FROM ").append(inTableName)
                .append(" WHERE ").append(outTableName).append(StringPool.DOT).append(outField).append(StringPool.EQUALS).append(inTableName).append(StringPool.DOT).append(inField)
                .append(" AND ").append(inTableName).append(StringPool.DOT).append("deleted").append(StringPool.EQUALS).append(false);
        return exists;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseSave(T info) {
        if (null == info) {
            return false;
        }
        return baseSave(Collections.singletonList(info));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseSave(Collection<? extends T> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        Long userId = LocalThreadContextUtils.getUserId();
        Set<Serializable> ids = list.stream().filter(o -> null != o.getId()).map(o -> o.getId()).collect(Collectors.toSet());
        List<T> oldInfos = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(ids)) {
            oldInfos = listByIds(ids);
        }
        Set<Object> existIds = oldInfos.stream().map(o -> o.getId()).collect(Collectors.toSet());
        List<T> addInfos = new ArrayList<>();
        List<T> updateInfos = new ArrayList<>();
        list.forEach(info -> {
            //数据库维护标准字段
            info.setCreateDate(null);
            info.setModifyDate(null);
            //没有指定更新人, 默认为当前操作人为更新人
            if (null == info.getModifier()) {
                info.setModifier(userId);
            }
            //没有指定ID, 生成一个ID
            if (null == info.getId()) {
                info.setId(IdGenUtil.longId());
            }
            if (existIds.contains(info.getId())) {
                updateInfos.add(info);
            } else {
                if (null == info.getCreator()) {
                    info.setCreator(userId);
                }
                addInfos.add(info);
            }
        });
        boolean saveBatch = true;
        boolean updateBatchById = true;
        if (ObjectUtil.isNotEmpty(addInfos)) {
            saveBatch = saveBatch(addInfos);
        }
        if (ObjectUtil.isNotEmpty(updateInfos)) {
            updateBatchById = updateBatchById(updateInfos);
        }
        return saveBatch && updateBatchById;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseAdd(T info) {
        if (null == info) {
            return false;
        }
        return baseAdd(Collections.singletonList(info));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseAdd(Collection<? extends T> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        Long userId = LocalThreadContextUtils.getUserId();
        List<T> addList = new ArrayList<>();
        list.forEach(info -> {
            //数据库维护标准字段
            info.setCreateDate(null);
            info.setModifyDate(null);
            //没有指定更新人, 默认为当前操作人为更新人
            if (null == info.getModifier()) {
                info.setModifier(userId);
            }
            //没有指定ID, 生成一个ID
            if (null == info.getId()) {
                info.setId(IdGenUtil.longId());
            }
            addList.add(info);
        });
        return saveBatch(addList);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseEdit(T info) {
        if (null == info) {
            return false;
        }
        return baseEdit(Collections.singletonList(info));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseEdit(Collection<? extends T> list) {
        if (ObjectUtil.isEmpty(list)) {
            return false;
        }
        boolean present = list.stream().anyMatch(o -> ObjectUtil.isEmpty(o.getId()));
        if (present) {
            throw new CustomException(ResponseCode.PARAM_INVALID);
        }
        Long userId = LocalThreadContextUtils.getUserId();
        List<T> updateInfos = new ArrayList<>();
        list.forEach(info -> {
            //数据库维护标准字段
            info.setCreateDate(null);
            info.setModifyDate(null);
            //没有指定更新人, 默认为当前操作人为更新人
            if (null == info.getModifier()) {
                info.setModifier(userId);
            }
            updateInfos.add(info);
        });
        return updateBatchById(updateInfos);
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseDel(Serializable id) {
        if (null == id) {
            return false;
        }
        return baseDel(Collections.singletonList(id));
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public boolean baseDel(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return false;
        }
        return removeByIds(ids);
    }

    @Override
    public Optional<T> baseFindById(Serializable id) {
        if (null == id) {
            return Optional.empty();
        }
        return listByIds(Collections.singletonList(id.toString())).stream().findAny();
    }

    @Override
    public List<T> baseFindById(Collection<? extends Serializable> id) {
        if (ObjectUtil.isEmpty(id)) {
            return Collections.emptyList();
        }
        return listByIds(id);
    }

    @Override
    public T baseGetById(Serializable id) {
        if (null == id) {
            throw new CustomException(ResponseCode.PARAM_INVALID);
        }
        T t = getById(id.toString());
        if (null == t) {
            throw new CustomException(ResponseCode.PARAM_INVALID);
        }
        return t;
    }

}

